home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / folder watching / fw receiver / appleevents.c next >
Encoding:
Text File  |  2000-06-23  |  16.9 KB  |  668 lines

  1. /*
  2.     File:        AppleEvents.c
  3.  
  4.     Contains:    The main routine
  5.  
  6.     Written by: Chris White    
  7.  
  8.     Copyright:    Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/21/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                     
  21.                 26/01/96    GS                FW Receiver
  22.                                             Added ability to accept and act on Apple events from
  23.                                             Folder Watcher FBA application.
  24.  
  25.                 12/18/95    CW                First release
  26.                 
  27.  
  28. */
  29.  
  30. // System Includes
  31.  
  32. #ifndef __APPLEEVENTS__
  33.     #include <AppleEvents.h>
  34. #endif
  35.  
  36. #include <ASRegistry.h>
  37. #include <Notification.h>
  38. #include <Icons.h>
  39. #include <Resources.h>
  40. #ifndef __SOUND__
  41.     #include <Sound.h>
  42. #endif
  43. #include <TextUtils.h>
  44.  
  45.  
  46.  
  47.  
  48. // Application includes
  49.  
  50. #ifndef __BAREBONES__
  51.     #include "BareBones.h"
  52. #endif
  53.  
  54. #ifndef __PROTOTYPES__
  55.     #include "Prototypes.h"
  56. #endif
  57.  
  58. #include <Resources.h>
  59.  
  60.  
  61. typedef struct MyNotificationRecord
  62. {
  63.     NMRec   notification;
  64.     Str255  notificationString;
  65. } MyNotificationRecord, *MyNotificationPtr;
  66.  
  67.  
  68.                 // The events sent by the Folder Watcher FBA
  69. #define    kFolderWatcherSuite        'wFWS'
  70.  
  71. enum
  72. {
  73.     kTypeFileAdded = 'wTFA', kTypeFileRemoved = 'wTFR', kTypeFileModified = 'wTFC'
  74. };
  75.  
  76.  
  77. const short        ChangeSTRs = 200;
  78. enum
  79. {
  80.     kAddedStr = 1, kRemovedStr, kModifiedStr
  81. };
  82.  
  83. const short        SubroutineSTRs = 300;
  84. enum
  85. {
  86.     kAddedSub = 1, kRemovedSub, kModifiedSub
  87. };
  88.  
  89. const short        NotificationIcon = 501;
  90.  
  91.     // Prototypes
  92. pascal OSErr HandleOpenApplication( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  93. pascal OSErr HandleQuitApplication( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  94. pascal OSErr HandleOpenDocuments( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  95. pascal OSErr HandlePrintDocuments( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  96.  
  97. pascal OSErr HandleFileAdded( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  98. pascal OSErr HandleFileRemoved( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  99. pascal OSErr HandleFileModified( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  100.  
  101. pascal OSErr HandleASSubroutine( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon );
  102.  
  103. void        DisplayChange( AEDesc* theDesc, short theChange );
  104. void        DisplayChangeDesc( AEDesc* theDesc, short theChange );
  105. void        AppendPStr( StringPtr sourceStr, StringPtr destStr );
  106. OSErr        GetDescriptorData( const AEDesc* theDesc, Ptr destPtr, Size maxSize );
  107. OSErr        FSSpecIsDirectory( FSSpec* theSpec, Boolean* theResult );
  108. OSErr        GetParentDirectoryName( FSSpec* theSpec, StringPtr theName );
  109.  
  110. void        InitNotification( void );
  111. OSErr        Notify( Boolean fSound, short iconID, StringPtr message );
  112. OSErr        InstallNotification( Boolean fSound, short iconID, StringPtr message );
  113. OSErr        RemoveNotification( void );
  114.  
  115. short        PositiveRandom( void );
  116.  
  117.  
  118.     // Globals
  119. Str255                gAddedSubroutineName;
  120. Str255                gRemovedSubroutineName;
  121. Str255                gModifiedSubroutineName;
  122.  
  123. NMUPP                gMyNMProcPtr = NULL;
  124. Boolean                gRemoveNotification;
  125. MyNotificationPtr    gNotificationPtr;
  126. short                gNumberSounds;
  127. SndChannelPtr        gSoundChannel;
  128.  
  129. #pragma segment Initialize
  130.  
  131. OSErr InstallAppleEventHandlers ( void )
  132. {
  133.     short err;
  134.     
  135.     InitNotification( );
  136.  
  137.     gAddedSubroutineName[0] = 0;
  138.     gRemovedSubroutineName[0] = 0;
  139.     gModifiedSubroutineName[0] = 0;
  140.     
  141.     err = AEInstallEventHandler ( kCoreEventClass, kAEOpenApplication,
  142.                         NewAEEventHandlerProc( HandleOpenApplication ), 0L, false );
  143.     if (noErr != err) goto done;
  144.  
  145.     err = AEInstallEventHandler ( kCoreEventClass, kAEQuitApplication,
  146.                         NewAEEventHandlerProc( HandleQuitApplication ), 0L, false );
  147.     if (noErr != err) goto done;
  148.  
  149.     err = AEInstallEventHandler ( kCoreEventClass, kAEOpenDocuments,
  150.                         NewAEEventHandlerProc (HandleOpenDocuments ), 0L, false );
  151.     if (noErr != err) goto done;
  152.  
  153.     err = AEInstallEventHandler ( kCoreEventClass, kAEPrintDocuments,
  154.                         NewAEEventHandlerProc( HandlePrintDocuments ), 0L, false );
  155.     if (noErr != err) goto done;
  156.                         
  157.                         // Events from Folder Watcher FBA
  158.                         
  159.     err = AEInstallEventHandler ( kFolderWatcherSuite, kTypeFileAdded,
  160.                         NewAEEventHandlerProc( HandleFileAdded ), 0L, false );
  161.     if (noErr != err) goto done;
  162.                         
  163.     err = AEInstallEventHandler ( kFolderWatcherSuite, kTypeFileRemoved,
  164.                         NewAEEventHandlerProc( HandleFileRemoved ), 0L, false );
  165.     if (noErr != err) goto done;
  166.                         
  167.     err = AEInstallEventHandler ( kFolderWatcherSuite, kTypeFileModified,
  168.                         NewAEEventHandlerProc( HandleFileModified ), 0L, false );
  169.     if (noErr != err) goto done;
  170.     
  171.                         // Events from Script Application
  172.  
  173.     err = AEInstallEventHandler ( kASAppleScriptSuite, kASSubroutineEvent,
  174.                         NewAEEventHandlerProc( HandleASSubroutine ), 0L, false );
  175.     if (noErr != err) goto done;
  176.  
  177. done:        
  178.     return err;
  179. }    // InstallAppleEventHandlers
  180.  
  181.  
  182.  
  183. #pragma segment Core
  184.  
  185. pascal OSErr HandleOpenApplication( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  186. {
  187. #ifdef __MWERKS__
  188.     #pragma unused (theAppleEvent, reply, refcon )
  189. #endif
  190.  
  191.     return noErr;
  192. }
  193.  
  194.  
  195.  
  196. pascal OSErr HandleQuitApplication( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  197. {
  198. #ifdef __MWERKS__
  199.     #pragma unused (theAppleEvent, reply, refcon )
  200. #endif
  201.  
  202.     OSErr    err;
  203.  
  204.     if ( gSoundChannel )
  205.     {
  206.         err = SndDisposeChannel( gSoundChannel, true );
  207.         gSoundChannel = NULL;
  208.     }
  209.  
  210.     gQuit = true;
  211.     return noErr;
  212. }
  213.  
  214.  
  215.  
  216. pascal OSErr HandleOpenDocuments( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  217. {
  218. #ifdef __MWERKS__
  219.     #pragma unused (theAppleEvent, reply, refcon )
  220. #endif
  221.  
  222.     return errAEEventNotHandled;
  223. }
  224.  
  225.  
  226.  
  227. pascal OSErr HandlePrintDocuments( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  228. {
  229. #ifdef __MWERKS__
  230.     #pragma unused (theAppleEvent, reply, refcon )
  231. #endif
  232.  
  233.     return errAEEventNotHandled;
  234. }
  235.  
  236.  
  237. pascal OSErr HandleFileAdded( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  238. {
  239. #ifdef __MWERKS__
  240.     #pragma unused ( reply, refcon )
  241. #endif
  242.  
  243.     AEDesc        aDesc = { typeNull, NULL };
  244.     OSErr        err;
  245.     
  246.     err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeWildCard, &aDesc );
  247.     if ( noErr != err) goto done;
  248.  
  249.     DisplayChange( &aDesc, kAddedStr );
  250.  
  251. done:
  252.     AEDisposeDesc( &aDesc );
  253.  
  254.     return err;
  255. }
  256.  
  257.  
  258. pascal OSErr HandleFileRemoved( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  259. {
  260. #ifdef __MWERKS__
  261.     #pragma unused ( reply, refcon )
  262. #endif
  263.  
  264.     AEDesc        aDesc = { typeNull, NULL };
  265.     OSErr        err;
  266.     
  267.     err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeWildCard, &aDesc );
  268.     if ( noErr != err) goto done;
  269.  
  270.     DisplayChange( &aDesc, kRemovedStr );
  271.  
  272. done:
  273.     AEDisposeDesc( &aDesc );
  274.  
  275.     return err;
  276. }
  277.  
  278.  
  279. pascal OSErr HandleFileModified( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  280. {
  281. #ifdef __MWERKS__
  282.     #pragma unused ( reply, refcon )
  283. #endif
  284.  
  285.     AEDesc        aDesc = { typeNull, NULL };
  286.     OSErr        err;
  287.     
  288.     err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeWildCard, &aDesc );
  289.     if ( noErr != err) goto done;
  290.  
  291.     DisplayChange( &aDesc, kModifiedStr );
  292.  
  293. done:
  294.     AEDisposeDesc( &aDesc );
  295.  
  296.     return err;
  297. }
  298.  
  299.  
  300. // AppleScript will send this type of event if you structure your call like a subroutine.
  301. // This way you can accept the events without AppleScript checking out your terminology
  302. // through the 'aete' resource.
  303.  
  304. pascal OSErr HandleASSubroutine( AppleEvent *theAppleEvent, AppleEvent *reply, long refcon )
  305. {
  306. #ifdef __MWERKS__
  307.     #pragma unused ( reply, refcon )
  308. #endif
  309.  
  310.     Str255        subroutineStr;
  311.     DescType    typeCode;
  312.     Size        actualSize;
  313.     AEDesc        aDesc = { typeNull, NULL };
  314.     OSErr        err;
  315.     
  316.     
  317.     err = AEGetParamPtr( theAppleEvent, keyASSubroutineName, typeChar,
  318.                                     &typeCode, &subroutineStr[1], 255, &actualSize );
  319.     if ( noErr != err ) goto done;
  320.     subroutineStr[0] = actualSize;
  321.     
  322.     err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeWildCard, &aDesc );
  323.     if ( noErr != err ) goto done;
  324.     
  325.                 // Check we've read the resources in
  326.     if ( ! gAddedSubroutineName[0] )
  327.         GetIndString( gAddedSubroutineName, SubroutineSTRs, kAddedSub );
  328.  
  329.     if ( ! gRemovedSubroutineName[0] )
  330.         GetIndString( gRemovedSubroutineName, SubroutineSTRs, kRemovedSub );
  331.  
  332.     if ( ! gModifiedSubroutineName[0] )
  333.         GetIndString( gModifiedSubroutineName, SubroutineSTRs, kModifiedSub );
  334.  
  335.     if ( EqualString( subroutineStr, gAddedSubroutineName , false, false ))
  336.         DisplayChange( &aDesc, kAddedStr );
  337.     else if ( EqualString( subroutineStr, gRemovedSubroutineName , false, false ))
  338.         DisplayChange( &aDesc, kRemovedStr );
  339.     else if ( EqualString( subroutineStr, gModifiedSubroutineName , false, false ))
  340.         DisplayChange( &aDesc, kModifiedStr );
  341.  
  342. done:
  343.     AEDisposeDesc( &aDesc );
  344.  
  345.     return err;
  346. }
  347.  
  348.  
  349. // This routine takes care of lists
  350.  
  351. void    DisplayChange( AEDesc* theDesc, short theChange )
  352. {
  353.     AEDesc        aDesc = { typeNull, NULL };
  354.     long        aCount,
  355.                 anIndex;
  356.     AEKeyword    anAEKeyword;
  357.     OSErr        err;
  358.  
  359.     switch ( theDesc->descriptorType )
  360.     {
  361.         case typeAEList:        // Handle lists
  362.             err = AECountItems( theDesc, &aCount );
  363.             if ( noErr != err ) goto done;
  364.             
  365.             for (anIndex = 1; anIndex <= aCount; anIndex++ )
  366.             {
  367.                 err = AEGetNthDesc( theDesc, anIndex, typeWildCard, &anAEKeyword, &aDesc );
  368.                 if ( noErr != err ) goto done;
  369.                 
  370.                 DisplayChange( &aDesc, theChange );
  371.             
  372.                 (void)AEDisposeDesc( &aDesc );
  373.             }
  374.             break;
  375.             
  376.         default:
  377.             DisplayChangeDesc( theDesc, theChange );
  378.     }
  379.     
  380. done:
  381.     (void)AEDisposeDesc( &aDesc );
  382. }
  383.  
  384.  
  385. // Add the change to our list and notify user of change.
  386.  
  387. void    DisplayChangeDesc( AEDesc* theDesc, short theChange )
  388. {
  389.     Str255            displayStr,
  390.                     tempStr;
  391.     AEDesc            anFSSpecDesc = { typeNull, NULL };
  392.     FSSpec            anFSSpec;
  393.     Boolean            fDirectory;
  394.     unsigned long    now;
  395.     OSErr            err;
  396.     
  397.     err = AECoerceDesc( theDesc, typeFSS, &anFSSpecDesc );
  398.     if ( noErr != err ) goto done;
  399.     
  400.     err = GetDescriptorData( &anFSSpecDesc, (Ptr)&anFSSpec, sizeof( anFSSpec ) );
  401.     if ( noErr != err ) goto done;
  402.     
  403.     err = FSSpecIsDirectory( &anFSSpec, &fDirectory);
  404.     if ( noErr != err ) goto done;
  405.  
  406.     displayStr[0] = 0;    // Empty the string
  407.     
  408.     if ( ! fDirectory )        // If it isn't a directory then add 
  409.     {                        // the directory name before it.
  410.         err = GetParentDirectoryName( &anFSSpec, tempStr );
  411.         if ( noErr != err ) goto done;
  412.         AppendPStr( tempStr, displayStr );
  413.         AppendPStr( "\p:", displayStr );
  414.     }
  415.     
  416.     AppendPStr( anFSSpec.name, displayStr );    // Add the folder/file name
  417.     if ( fDirectory )
  418.         AppendPStr( "\p:", displayStr );        // If a directory show by sticking colon after
  419.  
  420.     GetIndString( tempStr, ChangeSTRs, theChange );
  421.     AppendPStr( "\p\t", displayStr );            // Add a tab
  422.     AppendPStr( tempStr, displayStr );            // Add Added/Removed/Modified
  423.     
  424.     GetDateTime( &now );
  425.     TimeString( now, false, tempStr, NULL );
  426.     AppendPStr( "\p\t", displayStr );            // Add a tab
  427.     AppendPStr( tempStr, displayStr );            // Add Time
  428.     
  429.     DateString( now, abbrevDate, tempStr, NULL );
  430.     AppendPStr( "\p\t", displayStr );            // Add a tab
  431.     AppendPStr( tempStr, displayStr );            // Add Date
  432.     
  433.     AddToListContent( displayStr );
  434.     
  435.     Notify( true, NotificationIcon, NULL );
  436.     
  437. done:
  438.     AEDisposeDesc( &anFSSpecDesc );
  439. }
  440.  
  441.  
  442. // Just a pascal string concatenation routine.
  443. // Doesn't check for 255 character limit.
  444.  
  445. void    AppendPStr( StringPtr sourceStr, StringPtr destStr )
  446. {
  447.     BlockMoveData( &sourceStr[1], &destStr[1] + destStr[0], sourceStr[0] );
  448.     destStr[0] += sourceStr[0];
  449. }
  450.  
  451.  
  452. // Get the data out of the data handle of the descriptor.
  453.  
  454. OSErr    GetDescriptorData( const AEDesc* theDesc, Ptr destPtr, Size maxSize )
  455. {
  456.     Size    copySize;
  457.     OSErr    err = noErr;
  458.     
  459.     if ( typeNull == theDesc->descriptorType || ! theDesc->dataHandle )
  460.         return( errAENotAEDesc );
  461.  
  462.     copySize = GetHandleSize( (Handle)theDesc->dataHandle );
  463.     if ( copySize <= maxSize )
  464.     {
  465.         HLock( (Handle)theDesc->dataHandle );
  466.         BlockMoveData( *theDesc->dataHandle, destPtr, copySize );
  467.         HUnlock( (Handle)theDesc->dataHandle );
  468.     }
  469.     else
  470.         err = errAECorruptData;
  471.     
  472.     return err;
  473. } // GetDescriptorData
  474.  
  475.  
  476. // Check to see if FSSpec is for a directory.
  477.  
  478. OSErr    FSSpecIsDirectory( FSSpec* theSpec, Boolean* theResult )
  479. {
  480.     CInfoPBRec         aPB;
  481.     OSErr             err;
  482.  
  483.     aPB.dirInfo.ioNamePtr = theSpec->name;
  484.     aPB.dirInfo.ioVRefNum = theSpec->vRefNum;
  485.     aPB.dirInfo.ioDrDirID = theSpec->parID;
  486.     aPB.dirInfo.ioFDirIndex = 0;    // Use ioNamePtr and ioDirID
  487.     aPB.dirInfo.ioACUser = 0;        // Clear it before calling GetCatInfo
  488.     err = PBGetCatInfo( &aPB, false );
  489.     
  490.     if ( noErr == err )
  491.         *theResult = ( aPB.dirInfo.ioFlAttrib & ioDirMask );
  492.  
  493.     return err;
  494. }
  495.  
  496.  
  497. // Get parent directory name for an FSSpec.
  498.  
  499. OSErr    GetParentDirectoryName( FSSpec* theSpec, StringPtr theName )
  500. {
  501.     CInfoPBRec         aPB;
  502.     OSErr             err;
  503.  
  504.     aPB.dirInfo.ioNamePtr = theName;
  505.     aPB.dirInfo.ioVRefNum = theSpec->vRefNum;
  506.     aPB.dirInfo.ioDrDirID = theSpec->parID;
  507.     aPB.dirInfo.ioFDirIndex = -1;    // Get info about the directory
  508.     aPB.dirInfo.ioACUser = 0;    // Clear it before calling GetCatInfo
  509.     err = PBGetCatInfo( &aPB, false );
  510.  
  511.     return err;
  512. }
  513.  
  514.  
  515. // Call this once during application initialization
  516.  
  517. void    InitNotification( void )
  518. {
  519.     OSErr    err;
  520.  
  521.     gRemoveNotification = false;
  522.     GetDateTime( (unsigned long *)&qd.randSeed );
  523.     gNumberSounds = Count1Resources( soundListRsrc );
  524.     gSoundChannel = NULL;
  525.     err = SndNewChannel( &gSoundChannel, 0, 0, NULL );
  526. }
  527.  
  528.  
  529. // This routine checks whether or not the notification
  530. // is already displayed. If it isn't then it puts up
  531. // a notification.
  532.  
  533. OSErr    Notify( Boolean fSound, short iconID, StringPtr message )
  534. {
  535.     Handle        sndHandle;
  536.     OSErr       err = noErr;
  537.     
  538.     fSound = ( fSound && ( gNumberSounds > 0 ) );
  539.     
  540.     if ( gRemoveNotification && fSound)
  541.     {                            // We'll still play the sounds because I like it.
  542.                                 // Note that this may drive the user crazy.
  543.         sndHandle = Get1IndResource( soundListRsrc, ( PositiveRandom( ) % gNumberSounds ) + 1 );
  544.         HNoPurge( sndHandle );
  545.         err = SndPlay( gSoundChannel, (SndListHandle)sndHandle, true );
  546.     }
  547.     else
  548.         err = InstallNotification( true, iconID, message );
  549.         
  550.     return err;
  551. }
  552.  
  553.  
  554. OSErr    InstallNotification( Boolean fSound, short iconID, StringPtr message )
  555. {
  556.     Handle        anIconSuite;
  557.     Handle        ics8Handle,
  558.                 ics4Handle,
  559.                 icsBWHandle;
  560.     Handle        sndHandle;
  561.     OSErr       err;
  562.  
  563.     gNotificationPtr = (MyNotificationPtr)NewPtrClear( sizeof( MyNotificationRecord ) );
  564.     err = MemError( );
  565.     if (noErr != err || ! gNotificationPtr ) goto done;
  566.  
  567.     gNotificationPtr->notification.qType = nmType;
  568.     gNotificationPtr->notification.nmMark = 1;        // Put a mark next to our application
  569.                                                     // in the application menu.
  570.     if ( iconID )
  571.     {
  572.         err = NewIconSuite(&anIconSuite);
  573.         if (noErr != err ) goto done;
  574.         
  575.         ics8Handle = Get1Resource( kSmall8BitData, NotificationIcon );
  576.         HNoPurge( ics8Handle );
  577.         err = AddIconToSuite( ics8Handle, anIconSuite, kSmall8BitData );
  578.         if (noErr != err ) goto done;
  579.     
  580.         ics4Handle = Get1Resource( kSmall4BitData, NotificationIcon );
  581.         HNoPurge( ics4Handle );
  582.         err = AddIconToSuite( ics4Handle, anIconSuite, kSmall4BitData );
  583.         if (noErr != err ) goto done;
  584.     
  585.         icsBWHandle = Get1Resource( kSmall1BitMask, NotificationIcon );
  586.         HNoPurge( icsBWHandle );
  587.         err = AddIconToSuite( icsBWHandle, anIconSuite, kSmall1BitMask );
  588.         if (noErr != err ) goto done;
  589.     
  590.         gNotificationPtr->notification.nmIcon = anIconSuite;
  591.     }
  592.     else
  593.         gNotificationPtr->notification.nmIcon = NULL;
  594.  
  595.         // handle to sound record
  596.     if ( fSound )
  597.     {
  598.         sndHandle = Get1IndResource( soundListRsrc, ( PositiveRandom( ) % gNumberSounds ) + 1 );
  599.         HNoPurge( sndHandle );
  600.         gNotificationPtr->notification.nmSound = sndHandle;
  601.     }
  602.     else
  603.         gNotificationPtr->notification.nmSound = NULL;
  604.  
  605.         // string to appear in alert
  606.     if ( message )    // Send NULL StringPtr if no alert wanted
  607.     {
  608.         BlockMoveData( message, gNotificationPtr->notificationString, message[0] + 1 );
  609.         gNotificationPtr->notification.nmStr = gNotificationPtr->notificationString;
  610.     }
  611.     else
  612.         gNotificationPtr->notification.nmStr = NULL;
  613.         
  614.         // pointer to response routine
  615.     gNotificationPtr->notification.nmResp = NULL;    // gMyNMProcPtr;
  616.     gNotificationPtr->notification.nmRefCon = 0;    // SetCurrentA5( );    // Copy current A5
  617.  
  618.     err = NMInstall( (NMRecPtr)gNotificationPtr );
  619.     if ( noErr == err )
  620.         gRemoveNotification = true;
  621.  
  622. done:
  623.     if ( noErr != err )        // If there was an error then clean up the memory
  624.     {
  625.         if ( gNotificationPtr->notification.nmIcon )
  626.             err = DisposeIconSuite( gNotificationPtr->notification.nmIcon, false );
  627.         
  628.         DisposePtr( (Ptr)gNotificationPtr );
  629.         gNotificationPtr = NULL;
  630.     }
  631.  
  632.     return err;
  633. }
  634.  
  635.  
  636. // If there is a notification running then this routine removes it
  637. // and disposes of the associated memory.
  638.  
  639. OSErr    RemoveNotification( void )
  640. {
  641.     OSErr   err = noErr;
  642.     
  643.     if ( gRemoveNotification )
  644.     {
  645.         err = NMRemove( (NMRecPtr)gNotificationPtr );
  646.  
  647.         if ( gNotificationPtr->notification.nmIcon )
  648.             err = DisposeIconSuite( gNotificationPtr->notification.nmIcon, false );
  649.  
  650.         DisposePtr( (Ptr)gNotificationPtr );
  651.         gNotificationPtr = NULL;
  652.         
  653.         gRemoveNotification = false;        // Reset the flag
  654.     }
  655.  
  656.     return err;
  657. } // RemoveNotification
  658.  
  659.  
  660. short    PositiveRandom( void )
  661. {
  662.     short    aRandom = Random( );
  663.     
  664.     if ( aRandom >= 0 )
  665.         return aRandom;
  666.     else
  667.         return aRandom * -1;
  668. }